Devlog
These are largely just notes copied from my Status.md file after I'm done with them.
May 4, 2022
Still need to update enable_csrf(). I implemented csrf for all my form pages. I might want more robust tests to ensure these work but ... idunno that sounds like a lot of work lol. I might be okay with just manually testing them in the browser.
It would be cool as heck to add form tests to my php test lib ... where it would extract inputs from a <form> and build a request that essentially submits that form. But I don't think it's worth implementing that & switching this lib's tests over to that method, even though it would be better.
I also want to write a 'Browser' class for my test lib that would facilitate the kind of tests I'm doing here - the ones that require cookies & php session id & maybe make it easier to modify the request than just having a very long method signature ... but again ... i don't NEED these things at this point, unless I really want to finish getting 100% testing.
OH ... for manual testing the register/login/reset flow, i will need to disable the forced passing csrf thing ... so idk how to do that ... maybe with a user agent check? Or just by commenting it out, but then that's inconsistent
Looks like i can just check for the presence of a User Agent because curl does not send a user agent on its own, and the browser does.
So basically, i MIGHT need to improve testing for the CSRF token stuff. I definitely need to make the browser-based testing actually use the csrf system though (disable the force valid).
Still need to make a profile page, at least something VERY simple Still need to log ips i mean, all the TODOs listed below are probably necessary. I could probably do without the basic spam control, but i would like to have it. CSRF kind of covers it, maybe? But not completely. I'll need brief & probably different T&C for login, register, and reset password ... like you agree to ALL THE THINGS when you register, so you don't have to agree on the login screen ... but since i will log IPs on login, i WILL need to have them agree that their IP (and user-agent) will be logged in conjunction with the account they're trying to sign in to & that the owner of the account will be able to view this information, as well as the website administrators.
Same goes for password reset. Largely because the people trying to login or password reset may be people who haven't registered yet & there will be logging & in some cases, an email sent.
May 3, 2022
I implemented csrf backend (mostly). I have enable_csrf() & csrf_is_valid() methods on lib that handle all the session stuff. I wrote tests for both of these with custom routes on the test server to make the tests work.
Earlier, i prototyped if ($lia->csrf_fails())return; on each of the pages that use csrf. I'm going to need to improve this, though. I may just add something to the validation engine for csrf checking (& setting!) so it's part of my existing flows. (should i then also update login to use my validation engine? Yeah, probably)
DONE OH ... and i think csrf_is_valid() should also delete the session data on success. I'll need to add that.
TODO (relegated on apr 26, 2022)
general refactor ...
- DONE move
cookie_login()into the lib class - DONE add a 'from_row()' function to the user class
- DONE change how
cookie_nameis stored & referenced - DONE new_code(): & activate()
- logic for registration, password_reset, & cookie_login.
- DONE registration sets user->is_active
- DONE different expirations for each
- logic for registration, password_reset, & cookie_login.
- DONE test registration expiration
- DONE code & test password reset
- DONE code & test logout
- DONE Remove LilDb dependency (it's still a dev dependency lol)
- DONE password validation (simple method no Lib class with minor configurability. no built-in way to write your own validation function)
- DONE maybe create a namespaced function for
setcookie()so i can more effectively test things cookie related - DONE permissions
- DONE throttling
- MAYBE sort user code into traits - once i add permissions & throttling I probably need to do this
- DONE permissions are already in a trait
Other important stuff
- emails
- DONE registration
- DONE password reset
- security notices
- gui code & routing
- DONE Login
- DONE throttling login
- DONE view form
- DONE POST login success
- DONE GET login page while already logged in
- DONE POST login failure
- DONE logout landing page
- DONE register
- DONE register form
- DONE register error messages (email incorrect, password incorrect, already registered)
- DONE registration completed landing page
- test the fail conditions? Meh
- DONE reset password
- DONE submit password reset
- DONE complete password reset (clicking link in the email)
- DONE Login
Apr 22, 2022
I have switched the reset-password flow into using the validation engine described on april 21. I've also added throttling, both to REQUEST password change and COMPLETE set new password.
I would like to switch to the validation engine for both login & registration too. It will make for more testable, better, more maintainable code.
Currently the password reset pages don't check if there is a user logged in ... i think that is okay. But it's something to consider.
Some of my Register tests are failing that were previously working. Idk why. I don't think i care until after i update the code to the validation engine
Oh, and when emails fail on POST request password reset... i think i need to be displaying the form, which i'm not currently doing.
I also need to go through my recent notes, move them to devlog, and probably review & update my TODO list.
Apr 21, 2022
I've made MUCH progress on password resets. There is one test to finish for registration (trying to register an account that has been registered, but the expiration code is invalid)
There's quite a bit left to do for password resets. It is mostly monotonous, both in the tests & in the implementation. It's just a lot of different branches to take.
Something I'm REALLY disliking about how I'm coding this is ... it is not totally clear what the branching is. It is just ... it is confusing. The code blocks are long.
Right now, it's in a pretty procedural style. It would be nice to do a ... less procedural style so that i can verify each branch is actually doing what it is supposed to.
I'm thinking something along the lines of ... there are many different paths that we can go ... and i want code that makes it clear which path we are on ... so let's try to sample paths for the GET & POST complete.reset-password.code
GET/POST complete.reset-password.code
is_get
code_is_valid
else error
show_form
is_post
code_is_valid
else error
post_email_is_valid
else error, show_form
post_password_is_valid
else error, show_form
post_password_matches_confirm_password
else error, show_form
code_email_matches_post_email
else error, show_form
set_new_password
send_security_email
show_success_message
<?php
$v = new Validation($_GET, $_POST); //it could load from these itself ...
$is_done = $v->is_get()
->code_is_valid('error message')
->show_form();
if ($is_done)Return;
$is_done =
$v->is_post()
->code_is_valid('error message')
->post_email_is_valid('error message', [$v,'show_form'])
->post_password_is_valid('error message', [$v,'show_form'])
->post_password_matches_confirm_password('error', [$v,'show_form'])
->code_email_matches_post_email('error', [$v,'show_form'])
->set_new_password()
->send_security_email()
->show_message();
So my first instinct is to use this pseudo-code as code & map it to some php handling thingy ... but a probably "smart"er thing to do is to just write a little validation class (or add stuff to the lib class) that let's me easily call these methods. That's the better way to go. Ugggggh
Apr 19, 2022
I am currently working on the reset password gui tests. I'm writing the tests first, then writing the code to make them work. I hit a mental block on SubmitReset. It's just a lot of steps & it is late in the day & my brain often shuts down at this point. With a fresh mind, it should not be too difficult. Have patience & take it one step at a time.
Also, i may want to split that test into two or more tests. One that JUST verifies the completion. Maybe another that JUST verifies the form (or error) for the completion code. And maybe I want those two smaller UNIT tests AND I want the full test that does the entire process.
After I finish the password reset, there's quite a bit more on my list of stuff to do. Most of it can be put off until a later date. I WILL need to :
- disable pages
- csrf token (Won't this break all my current tests!?)
- t&c + privacy policy
- mvp profile page
At least I think I need all those things. I would love to write the admin stuff, but I can just leave it to the dev to write code to setup admin stuff. That's a crummy way to do things, but it will work. Or i could just make a SUPER simple api+form for it. Idunno.
I could use permissions for page enabling/disabling. In that case, I would need some default permissions feature, such as to apply permissions to guest users who are not logged in
Apr 15, 2022
See TODO below
I nearly completed the registration tests, but i got hung up on validating email sending.
I have an approach that lets /usr/sbin/sendmail fail & store message in /var/spool/mail/reed. It's fine. It works. But it's awful & messy.
I need to verify email sending for UserExists test still. CreateUser seems to be good to go!
A big downfall of the current sendmail approach is requiring a long sleep() for sendmail to finish its thing. It also may be VERY dependent upon the system.
I could implement a custom sendmail script that outputs the email to disk in the project & then verify against the log file. This would surely be more efficient
I could alternatively use namespaces and define a custom mail() function & do roughly the same thing, but with less overhead.
I may want to make a more generic way to do the mail testing too, since I have to also do it for password resets & security notices (if i'm going to send security notices)
Its a mess!
Oh. And I'm not yet recording IP addresses, which seems like an important security thing. Google does it, so that i can review what IPs logged into my account. Github does. I think gitlab does too. Idk. it seems like a good thing to add.
Apr 12, 2022
I worked on server tests today & had to pause during the PostLogin test to update the testing lib to retrieve headers so i could test cookies & make sure login actually works ...
Below in this doc i wrote a (hopefully exhaustive) list of tasks todo & pages to build.
I renamed public/login.php to public/@POST.@GET.login.php bc idk it seems easier that way
I worked on Liaison to make packages accessible in views & public scripts
March 7, 2022
Throttling works! & permissions are done. Next up is gui stuff & routing. let's keep the liaison integration layer as minimal as possible. Like a single method to setup the liaison integration. And then hopefully it would be easy to set up some other system for routing & such.
And then I need to clean everything up (i could do that first). Delete old code & such.
Umm. There will be some feature & performance things I can add in the future.
OH. Security. I need CSRF. And some kind of spam control (honey pots or something)
it also might be good to add user_id throttling. currently it is just ip-throttling. adding user-id to it could help prevent distributed password brute force attacks. Though i could use the current system with two throttle calls.
March 31, 2022 end of day
I made a lot of progress on the user class. registration, activation, password login & cookie login all work!! (I still need to test the actual function of the cookie headers in future server tests)
I have removed the sentinel dependency & I think everything's going pretty smoothly! I definitely need som refactor. Big one is the new_code() feature that generates codes for registration, password_reset, and cookie_login. I need to test registration code expiration too.
Next steps are probably to test logging out, test password reset, and ... hmmm idk.
I think after i have logout, password reset, and the refactor, i should be able to start updating my gui code!
I will still need to setup permissions.
Throttling is a VERY good idea. I could throw in a VERY simple throttle implementation for the MVP.
User code will need to be sorted into traits still.
I need a standard way to load a row into a user object
I'm iffy about having cookie_login() ON the user object, since ... well it's just confusing. I think that part should be on the Lib class.
I'm using LilDb in a couple places .. i'd like to remove that dependency.
For the random code stuff, I need slightly different logic, i think? Because the registration code logic will set user->is_active. All different code types should have configurable expirations too. I don't mind defaults, but at least having public properties would be nice. I also want to minimize the clutter on the class!!! Uggh
Overall, I think this is going quite well. I'm glad I ditched sentinel & I'm having quite a nice time re-writing the user-backend ... basically from scratch.
March 31, 2022
I was making good progress on tests. Then I was unable to test cookies for logging in a user, becaues of Sentinel's design. I also have a hacky solution to syncing the email (and other properties) between the sentinel model & the user object.
I felt this was becoming more trouble than it was worth, especially since I've been wanting to kick Sentinel for awhile anyway.
So now I begin transitioning away from Sentinel. I'm not sure the design I'm going for ... just gonna replace it piece by piece & work on the registration test
March 30, 2022 END OF DAY
I have figured out the user problem!
I will likely have two user classes. One for users with credentials (email at least), and one for generic/temporary users. Temp users will have a much simpler interface, so there won't be user-ship overhead for most visitors.
The User class will do basically everything that affects the user. The Lib class will get_user($email) and other things that are not about an already instantiated user. i.e. Lib does not change emails, names, passwords, registration, or permissions. That's all for the User class.
In theory, this is a poor approach because a real life user doesn't set their own permissions, but I don't care. This approach will make a nice, easy-to-use API.
I will also be sorting user code into traits. For now, I'm just sectioning different pieces inside the User class (such as a 'status' section that involves registration, login, activation, etc. I'll have another for permissions)
I would REALLY like to get rid of sentinel. But this is not the priority. I gotta get all my tests going first.
I also saw that I had an API class & ... i don't think I liked that very much.
Anyway. Continue in test/run/Main.php in testRegisterUser. The test is going well. Next up is testing activation.
Problem
I'm having a hard time deciding what should go in user & what should go on the library class
In one hand, I want to put most stuff on the user, because ... then you just have to interact with the user to change or query anything about the user (including their permissions)
On the other hand, it's not the user's responsibility to update their own permissions.
But that's theory & it doesn't matter. API wise, I think it will be easier to have the methods that directly deal with a user ON the user.
This means the user class will be somewhat bloated. I'll organize different pieces into traits. (permissions)
login & registration must go on the library class ... because there is no user at that point. Unless I get_user() with the email, then register()
& in that case, every request might have a user.
So the library class then JUST holds the get_user() function & other static-type things.
Because I don't want the User class to load users ... that just seems confusing??
Another issue I have with this approach is that ... now the user has to be aware of the database. I don't WANT the user to be aware of the database. But this will probably make things much easier. So, whatever, I guess
--
And I don't know what to do about temporary users. I'll be using a temporary user to check if permission is granted & whatnot.
But a temporary user will not have a model and will not need a database connection.
It's possible I could allow temporary users to do some things ... but probably not
... i could allow in-active users to do some things, but not allow temporary to
So if I use the same object for both, then I need ... probably a lot of conditionals to literally just check if the user is temp & then return false (instead of doing the normal check)
Which makes me think I should have two user classes. A separate one for the TemporaryUsers with simple implementation that ... basically just returns false all the time.
At which point I need an interface
... but I'm not doing all of that right now
so maybe i should focus on what I am doing
which is writing tests for the user class
March 29, 2022
I updated the dependencies. I started setting up tests & a cli interface.
I wrote some notes in the test file.
Next up is to start writing tests. I want to initialize the user database.
Idunno what else I'll do.
I might add LilMigrations
I plan to kick Sentinel, after I have some tests setup & everything is basically working.
But Idunno. Sentinel does things that are convenient and that I'm not THRILLED to rewrite. But It is also just so much bloat, and so much exploitable surface area.
And it's probably a LOT slower than an implementation I would write up, since I tend to go very simple.
I also would start having more control & maybe more easily being able to setup the features I want.
I also MIIIIIGHT go full-phad. Idunno. It is more bloat ... but also like ... not that much, since the items are compiled.